﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

using Newtonsoft.Json;

using Server.Models;
using Server.Utils;

using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketBase.Protocol;

namespace Server.Core.Service
{
    public static class NetworkEngine
    {
        private static NetServer _tcpServer; // 服务器实例
        private static readonly ConfigModel _cfg = new ConfigModel();
        private static readonly ConcurrentDictionary<string, NetSession> _onlineList = new ConcurrentDictionary<string, NetSession>(); // 在线会话列表
        private static ConcurrentDictionary<string, UserModel> _userList = new ConcurrentDictionary<string, UserModel>(); // 用户列表
        private static readonly Queue<MessageModel> _pendingSendQueue = new Queue<MessageModel>(); // 待保存数据包
        private static readonly Queue<MessageModel> _pendingSaveQueue = new Queue<MessageModel>(); // 待广播数据
        private static StateModel _state = new StateModel(); // 状态
        private static System.Timers.Timer _castTimer; // 广播定时器
        private static System.Timers.Timer _dbTimer; // 写数据库定时器

        #region 构造函数

        public static void StartServer()
        {
            GetConfig();
            DBHelper.ConnString = $"Server={_cfg.DbConfig.DbServer};port={_cfg.DbConfig.DbPort};uid={_cfg.DbConfig.DbUser};pwd={_cfg.DbConfig.DbPass};charset=utf8;database={_cfg.DbConfig.DbName}";
            Console.WriteLine("初始化数据库");
            var db = DBHelper.Get();
            db.DbMaintenance.CreateDatabase();
            db.CodeFirst.InitTables(
                            typeof(UserModel),
                            typeof(StateModel),
                            typeof(MessageModel)
                            );

            Console.WriteLine("加载初始数据");
            LoadData();
            Console.WriteLine("初始化网络服务...");
            _tcpServer = new NetServer();
            ConfigServer();
            Console.WriteLine("启动存储定时器");
            _dbTimer = new System.Timers.Timer()
            {
                AutoReset = true,
                Interval = 1000,
                Enabled = true
            };
            _dbTimer.Elapsed += DBSave_Event;
            Console.WriteLine("启动广播定时器");
            _castTimer = new System.Timers.Timer
            {
                AutoReset = true,
                Interval = 1000,
                Enabled = true
            };
            _castTimer.Elapsed += CastTimer_Elapsed;
            //System.Timers.Timer t = new System.Timers.Timer { AutoReset = true, Interval = 3000, Enabled = true };
            //t.Elapsed += (a, b) =>
            //{
            //    _pendingSendQueue.Enqueue(new MessageModel
            //    {
            //        Data = Encoding.UTF8.GetBytes("现在时间:" + DateTime.Now.ToString()),
            //        ErrCode = "",
            //        MessageId = 8,
            //        Result = true,
            //        SendDate = DateTime.Now
            //    });
            //};
        }

        private static void CastTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            _castTimer.Stop();
            SendCast();
        }

        #endregion 构造函数

        #region 配置服务器参数

        private static void ConfigServer()
        {
            Console.WriteLine("初始化网络服务...");
            if (_tcpServer.Setup(rootConfig: _cfg.RootConfig, config: _cfg.ServerConfig))
            {
                Console.WriteLine("启动服务...");
                if (_tcpServer.Start())
                {
                    // 连接
                    _tcpServer.NewSessionConnected += TcpServer_NewSessionConnected;
                    // 新请求
                    _tcpServer.NewRequestReceived += TcpServer_NewRequestReceived;
                    // 断开连接
                    _tcpServer.SessionClosed += TcpServer_SessionClosed;
                    Console.WriteLine("网络服务启动成功");
                }
                else
                {
                    Console.WriteLine("初始化网络服务失败!");
                }
            }
            else
            {
                Console.WriteLine("配置网络服务失败!");
            }
        }

        private static void TcpServer_SessionClosed(NetSession session, CloseReason value)
        {
            Console.WriteLine($"客户端下线,EP:{session.RemoteEndPoint},用户名:{(session.UserInfo is null ? "" : session.UserInfo.UserName)}");
            if (_onlineList.Values.Contains(session))
            {
                var removeSession = _onlineList.FirstOrDefault(p => p.Value == session);
                _onlineList.TryRemove(removeSession.Key, out NetSession csession);
            }
        }

        private static void TcpServer_NewRequestReceived(NetSession session, StringRequestInfo requestInfo)
        {
            try
            {
                string jsonStr = "";
                switch (requestInfo.Key)
                {
                    case "heartbeat":
                        break;

                    case "login":
                        string data = requestInfo.Body;
                        if (string.IsNullOrEmpty(data))
                            return;
                        data = Encoding.UTF8.GetString(Convert.FromBase64String(data));
                        MessageModel msgData = JsonConvert.DeserializeObject<MessageModel>(data);
                        jsonStr = Encoding.UTF8.GetString(msgData.Data);
                        if (string.IsNullOrEmpty(jsonStr))
                        {
                            var msg = new MessageModel { Data = null, Result = false, ErrCode = "0001", ErrString = "用户名不能为空" };
                            var retStr = JsonConvert.SerializeObject(msg);
                            session.Send("loginresult " + retStr + Environment.NewLine);
                        }
                        else
                        {
                            SendDataHistory(jsonStr, session);
                        }
                        break;

                    case "publish":
                        data = requestInfo.Body;
                        if (string.IsNullOrEmpty(data))
                            return;
                        data = Encoding.UTF8.GetString(Convert.FromBase64String(data));
                        msgData = JsonConvert.DeserializeObject<MessageModel>(data);
                        msgData.MessageId = ++_state.MaxIndex;
                        msgData.UserId = session.UserInfo.UserId;
                        _pendingSaveQueue.Enqueue(msgData);
                        _pendingSendQueue.Enqueue(msgData);
                        break;

                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("接收数据出错:" + ex.Message);
            }
        }

        private static void TcpServer_NewSessionConnected(NetSession session)
        {
            Console.WriteLine($"新客户端连接,EP:{session.RemoteEndPoint}");
        }

        #endregion 配置服务器参数

        #region 读取配置文件

        private static void GetConfig()
        {
            Console.WriteLine("读取配置文件...");
            var fileName = AppDomain.CurrentDomain.BaseDirectory + "\\Config.json";
            void newConfig()
            {
                Console.WriteLine("初始化默认配置...");
                _cfg.ServerConfig = new ServerConfig
                {
                    Name = "Message Wild Cast Server",//服务器实例的名称
                    Ip = "Any",//服务器监听的ip地址。
                    Mode = SocketMode.Tcp,//Socket服务器运行的模式;
                    Port = 1955,//服务器监听的端口;
                    SendingQueueSize = 5000,//发送队列最大长度, 默认值为5;
                    MaxConnectionNumber = 5000,//可允许连接的最大连接数;
                    LogCommand = false,//是否记录命令执行的记录;
                    LogBasicSessionActivity = false,// 是否记录session的基本活动，如连接和断开;
                    LogAllSocketException = false,// 是否记录所有socket异常活动;
                    MaxRequestLength = 81920,//最大允许的请求长度，默认值为1024;
                    TextEncoding = "UTF-8",//文本的默认编码;
                    KeepAliveTime = 60,//keep alive数据的发送间隔秒数;
                    KeepAliveInterval = 10,// Keep alive失败之后, keep alive探测包的发送间隔秒;
                    ClearIdleSession = false,//是否定时清空空闲会话;
                    ClearIdleSessionInterval = 600,//清空空闲会话的时间间隔, 默认值是120, 单位为秒;
                    SyncSend = true,//是否启用同步发送模式, 默认值: false;
                };
                _cfg.RootConfig = new RootConfig
                {
                    MaxWorkingThreads = 5000,//线程池最大工作线程数量;
                    MinWorkingThreads = 10,//线程池最小工作线程数量;
                    MaxCompletionPortThreads = 5000,//线程池最大完成端口线程数量;
                    MinCompletionPortThreads = 10,//线程池最小完成端口线程数量;
                    DisablePerformanceDataCollector = true,//是否禁用性能数据采集;
                    PerformanceDataCollectInterval = 60,//性能数据采集频率 (单位为秒, 默认值: 60);
                    LogFactory = "",//默认logFactory的名字, 所有可用的 log factories定义在子节点
                    Isolation = IsolationMode.AppDomain
                };
                _cfg.DbConfig = new DbConfigModel
                {
                    DbName = "messagedb",
                    DbPass = "123456",
                    DbPort = "3306",
                    DbServer = "localhost",
                    DbUser = "root"
                };
            }
            if (!File.Exists(fileName))
            {
                newConfig();
                string jsonStr = JsonConvert.SerializeObject(_cfg);
                File.WriteAllText(fileName, jsonStr, Encoding.UTF8);
            }
            else
            {
                string jsonStr = File.ReadAllText(fileName, Encoding.UTF8);
                if (string.IsNullOrEmpty(jsonStr))
                {
                    newConfig();
                    jsonStr = JsonConvert.SerializeObject(_cfg);
                    File.WriteAllText(fileName, jsonStr, Encoding.UTF8);
                }
                else
                {
                    var cfg = JsonConvert.DeserializeObject<ConfigModel>(jsonStr);
                    _cfg.ServerConfig = cfg.ServerConfig;
                    _cfg.RootConfig = cfg.RootConfig;
                    _cfg.DbConfig = cfg.DbConfig;
                    Console.WriteLine("读取配置完成");
                }
            }
        }

        #endregion 读取配置文件

        #region 用户列表

        private static void GetUserList()
        {
            var lst = DBHelper.Get().Queryable<UserModel>().ToList();
            _userList.Clear();
            if (lst.Count > 0)
                lst.ForEach(itm =>
                {
                    _userList[itm.UserName] = itm;
                });
        }

        private static void SaveUserList()
        {
            var lst = _userList.Values.ToList();
            var db = DBHelper.Get();
            db.Updateable(lst).ExecuteCommand();
            GetUserList();
        }

        #endregion 用户列表

        #region 发送历史数据包

        private static object locker = new object();

        private async static void SendDataHistory(string userName, NetSession session)
        {
            await Task.Run(() =>
            {
                lock (locker)
                {
                    var db = DBHelper.Get();
                    var user = db.Queryable<UserModel>().First(p => p.UserName == userName);
                    if (user is null)
                    {
                        user = new UserModel
                        {
                            UserName = userName,
                            LastIndex = 0,
                            UserId = 0
                        };
                        user = db.Insertable(user).ExecuteReturnEntity();
                        _userList[user.UserName] = user;
                    }
                    var retStr = JsonConvert.SerializeObject(new MessageModel { Result = true });
                    retStr = "loginresult " + Convert.ToBase64String(Encoding.UTF8.GetBytes(retStr)) + Environment.NewLine;
                    session.Send(retStr);
                    // 检查用户没有收到的包
                    var lst = db.Queryable<MessageModel>().Where(p => p.MessageId > user.LastIndex).ToList();
                    session.UserInfo = user;

                    //if (_pendingSendQueue.Count > 0)
                    //    lst.AddRange(_pendingSendQueue.ToList());
                    // 加到在线列表
                    if (_onlineList.ContainsKey(user.UserName))
                    {
                        _onlineList.TryRemove(user.UserName, out NetSession ns);
                        ns.Close();
                    }
                    _onlineList.TryAdd(user.UserName, session);
                    Console.WriteLine($"客户端用户已登录, 用户名:{user.UserName}, EP:{session.RemoteEndPoint}");
                    if (lst.Count > 0)
                        lst.ForEach(itm =>
                        {
                            if (itm.UserId == user.UserId)
                            {
                                _userList[user.UserName].LastIndex = itm.MessageId;
                            }
                            else
                            {
                                var packStr = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(itm)));
                                session.Send("wcast " + packStr + Environment.NewLine);
                                _userList[user.UserName].LastIndex = itm.MessageId;
                            }
                        });
                    SaveUserList();
                }
            });
        }

        #endregion 发送历史数据包

        #region 定时存数据库

        private static void DBSave_Event(object sender, ElapsedEventArgs e)
        {
            SaveUserList();
            SaveData();
        }

        #endregion 定时存数据库

        #region 保存数据

        private static void SaveData()
        {
            try
            {
                var db = DBHelper.Get();
                // 保存数据包
                if (_pendingSaveQueue.Count > 0)
                {
                    List<MessageModel> msgList = new List<MessageModel>();
                    while (_pendingSaveQueue.Count > 0)
                        msgList.Add(_pendingSaveQueue.Dequeue());
                    db.Insertable(msgList).ExecuteCommand();
                }
                // 保存状态
                db.Updateable(_state).ExecuteCommand();
            }
            catch (Exception)
            {
            }
        }

        #endregion 保存数据

        #region 读取初始数据

        private static void LoadData()
        {
            var db = DBHelper.Get();
            // 上次关闭时的状态
            _state = db.Queryable<StateModel>().First();
            if (_state is null)
                _state = db.Insertable(new StateModel()).ExecuteReturnEntity();
            int maxIdx = _state.MaxIndex;
            var pendingList = db.Queryable<MessageModel>().Where(p => p.MessageId > maxIdx).ToList();
            // 上次未发送完的数据
            if (pendingList.Count > 0)
                pendingList.ForEach(itm =>
                {
                    _pendingSendQueue.Enqueue(itm);
                });
            // 读取用户列表
            GetUserList();
        }

        #endregion 读取初始数据

        #region 发送广播

        private async static void SendCast()
        {
            try
            {
                await Task.Run(() =>
                 {
                     if (_pendingSendQueue.Count > 0)
                     {
                         while (_pendingSendQueue.Count > 0)
                         {
                             var data = _pendingSendQueue.Dequeue();
                             string msgStr = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)));
                             foreach (var kv in _onlineList)
                             {
                                 var session = kv.Value;
                                 var kvs = _userList.FirstOrDefault(p => p.Value.UserName == kv.Key);
                                 var user = kvs.Value;
                                 if (data.UserId == user.UserId)
                                     continue;
                                 session.UserInfo.LastIndex = data.MessageId;
                                 user.LastIndex = data.MessageId;
                                 session.Send($"wcast {msgStr}{Environment.NewLine}");
                             }
                         }
                     }
                 });
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送广播出错:" + ex.Message);
            }
            _castTimer.Start();
        }

        #endregion 发送广播
    }
}